#include "gmssl/sm9.h"
#include "gmssl/zuc.h"
#include "log.h"
#include "napi/native_api.h"
#include "str_hex.h"
#include <algorithm>
#include <random>
#include <vector>

static napi_value argv[8] = {0};
static size_t argc = 0;
static napi_value thisVar = nullptr;
static void *data = nullptr;

struct NativeAsyncContext {
    napi_env env = nullptr;
    napi_async_work work = nullptr;
    napi_deferred deferred = nullptr; // promise
    napi_ref callbackRef = nullptr;   // callback
    void *data = nullptr;             // C++执行结果缓存区
    int dataLen = 0;                  // C++执行结果缓存区长度
    int ret = 0;                      // C++执行结果错误码
    napi_value argv[8] = {0};         // callback或promise返回的参数数据
    size_t argc = 0;                  // callback返回的参数长度
};

#define DECLARE_NAPI_FUNCTION(name, method)                                                                            \
    { name, nullptr, method, nullptr, nullptr, nullptr, napi_default, nullptr }

napi_value Constructor(napi_env env, napi_callback_info info) {
    napi_value thisVar = nullptr;
    void *data = nullptr;
    napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
    return thisVar;
}

static napi_value NAPI_Global_GetRandomBuffer(napi_env env, napi_callback_info info) {
    memset(argv, 0, sizeof(argv));
    argc = 2;
    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);

    NativeAsyncContext *context = new NativeAsyncContext();
    context->env = env;
    void *data = nullptr;
    context->dataLen = 4096;
    napi_get_arraybuffer_info(env, argv[0], &data, (size_t *)&context->dataLen);

    if (context->dataLen > 0) {
        context->data = new unsigned char[context->dataLen + 1];
        memcpy(context->data, data, context->dataLen);
    }

    napi_create_reference(env, argv[1], 1, &context->callbackRef);

    napi_value resource = nullptr;
    napi_create_string_utf8(env, "NAPI_Global_GetRandomBuffer", NAPI_AUTO_LENGTH, &resource);
    napi_create_async_work(
        env, nullptr, resource,
        // 回调1：此回调由napi异步执行，里面就是需要异步执行的业务逻辑。由于是异步线程执行，所以不要在此通过napi接口操作JS对象。
        [](napi_env env, void *data) {
            auto context = (NativeAsyncContext *)data;
            // C++执行
            if (context->data) {
                std::vector<unsigned char> vec;
                vec.resize(context->dataLen);
                memcpy(&vec[0], context->data, context->dataLen);
                LOG_PRINT_HEX("GetRandomBuffer data:", &vec[0], vec.size());
                std::random_device rd;  // 硬件生成随机种子
                std::mt19937 gen(rd()); // 梅森旋转算法生成器
                std::shuffle(vec.begin(), vec.end(), gen);
                memcpy(context->data, &vec[0], context->dataLen);
                LOG_PRINT_HEX("GetRandomBuffer data:", &vec[0], vec.size());
            }
            context->ret = 0;
        },
        // 回调2：此回调在上述异步回调执行完后执行，此时回到了JS线程来回调开发者传入的回调
        [](napi_env env, napi_status status, void *data) {
            auto context = (NativeAsyncContext *)data;
            // 生成错误码
            napi_create_int32(env, context->ret, &context->argv[context->argc++]);
            // 拷贝执行结果
            if (context->ret == 0) {
                void *data = nullptr;
                napi_create_arraybuffer(env, context->dataLen, &data, &context->argv[context->argc++]);
                memcpy(data, context->data, context->dataLen);
                delete[] (char *)context->data;
                context->data = nullptr;
            }
            // 通过napi_call_function调用callback回调返回结果
            napi_value callback = nullptr;
            napi_value returnVal = nullptr;
            napi_get_reference_value(env, context->callbackRef, &callback);
            // 回调结果参数
            napi_call_function(env, nullptr, callback, context->argc, context->argv, &returnVal);
            napi_delete_reference(env, context->callbackRef);
            napi_delete_async_work(env, context->work);
            delete context;
            context = nullptr;
        },
        (void *)context, &context->work);
    napi_queue_async_work(env, context->work);

    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

static napi_value NAPI_Global_GetRandomBufferSync(napi_env env, napi_callback_info info) {
    memset(argv, 0, sizeof(argv));
    argc = 1;
    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);

    void *data = nullptr;
    size_t dataLen = 4096;
    napi_get_arraybuffer_info(env, argv[0], &data, &dataLen);

    std::vector<unsigned char> vec;
    vec.resize(dataLen);
    memcpy(&vec[0], data, dataLen);
    LOG_PRINT_HEX("GetRandomBuffer data:", &vec[0], vec.size());
    std::random_device rd;  // 硬件生成随机种子
    std::mt19937 gen(rd()); // 梅森旋转算法生成器
    std::shuffle(vec.begin(), vec.end(), gen);
    LOG_PRINT_HEX("GetRandomBuffer data:", &vec[0], vec.size());

    napi_value result = nullptr;
    napi_create_arraybuffer(env, dataLen, &data, &result);
    memcpy(data, &vec[0], dataLen);
    return result;
}

static napi_value NAPI_Global_ZucCrypto(napi_env env, napi_callback_info info) {
    memset(argv, 0, sizeof(argv));
    argc = 3;
    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);

    char buff[4096] = {0};
    size_t buffLen = 0;

    do {
        unsigned char key[128] = {0};
        napi_get_value_string_utf8(env, argv[0], buff, sizeof(key), &buffLen);
        hexstr_to_byte(buff, buffLen, key);
        LOG_PRINT_HEX("ZucCrypto key:", key, buffLen / 2)

        unsigned char iv[128] = {0};
        napi_get_value_string_utf8(env, argv[1], buff, sizeof(iv), &buffLen);
        hexstr_to_byte(buff, buffLen, iv);
        LOG_PRINT_HEX("ZucCrypto iv:", iv, buffLen / 2)

        unsigned char input[2048] = {0};
        napi_get_value_string_utf8(env, argv[2], buff, sizeof(input), &buffLen);
        hexstr_to_byte(buff, buffLen, input);
        size_t inputLen = buffLen / 2;
        LOG_PRINT_HEX("ZucCrypto input:", input, inputLen)

        unsigned char output[2048 + 32] = {0};
        size_t outputLen = sizeof(output);

        ZUC_CTX zuc_ctx;
        zuc_encrypt_init(&zuc_ctx, key, iv);
        zuc_encrypt_update(&zuc_ctx, input, inputLen, output, &outputLen);
        LOG_PRINT_HEX("ZucCrypto output:", output, outputLen)

        byte_to_hexstr(output, outputLen, buff);
        buffLen = outputLen * 2;
    } while (0);

    napi_value result = nullptr;
    napi_create_string_utf8(env, buff, buffLen, &result);
    return result;
}

static napi_value NAPI_Global_GenSm9KeyPair(napi_env env, napi_callback_info info) {
    memset(argv, 0, sizeof(argv));
    argc = 3;
    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);

    char buff[4096] = {0};
    size_t buffLen = 0;

    do {
        int type = 0; // 0x01 签名主密钥，0x02 签名密钥，0x11 加解密主密钥，0x12 加解密密钥
        napi_get_value_int32(env, argv[0], &type);
        if (type != 0x01 && type != 0x02 && type != 0x11 & type != 0x12) {
            LOG_PRINT("GenSm9KeyPair type:%{public}d error", type);
            break;
        }

        if (type == 0x01) { // 签名主密钥
            SM9_SIGN_MASTER_KEY sign_master = {0};
            // 生成签名主密钥
            if (sm9_sign_master_key_generate(&sign_master) != 1) {
                LOG_PRINT("GenSm9KeyPair sm9_sign_master_key_generate error");
                break;
            }
            uint8_t buf[SM9_SIGN_MASTER_KEY_MAX_SIZE] = {0};
            size_t bufLen = 0;
            uint8_t *p = buf;
            // 将生成的签名主密钥数据序列化导出,公钥将X,Y值根据Z值计算得到x,y值。私钥ks值不变
            // 注：导出公钥仅包含x,y值。还原时进行归一化操作:当Z=1时，射影坐标等价于仿射坐标;
            // 公钥比对不能进行单纯的内存比对，可使用函数sm9_twist_point_equ
            if (sm9_sign_master_key_to_der(&sign_master, &p, &bufLen) != 1) {
                LOG_PRINT("GenSm9KeyPair sm9_sign_master_key_to_der error");
                break;
            }
            LOG_PRINT_HEX("sign_master key:", buf, bufLen);
            // 得到密钥数据，示例 30 81 A7 0220 私钥(32字节) 03818200 04 公钥(x+y=128字节)
            // 拷贝密钥数据，返回结果
            byte_to_hexstr(buf, bufLen, buff);
            buffLen = bufLen * 2;
            break;
        }

        if (type == 0x02) { // 签名密钥
            // 获取签名主密钥数据
            uint8_t key[2048] = {0};
            napi_get_value_string_utf8(env, argv[1], buff, sizeof(key), &buffLen);
            hexstr_to_byte(buff, buffLen, key);
            size_t keyLen = buffLen / 2;
            LOG_PRINT_HEX("GenSm9KeyPair key:", key, keyLen)
            const uint8_t *cp = key;
            SM9_SIGN_MASTER_KEY sign_master = {0};
            // 生成签名主密钥
            if (sm9_sign_master_key_from_der(&sign_master, &cp, &keyLen) != 1) {
                LOG_PRINT("GenSm9KeyPair sm9_sign_master_key_from_der error");
                break;
            }
            // 获取ID数据
            unsigned char id[2048] = {0};
            napi_get_value_string_utf8(env, argv[2], buff, sizeof(id), &buffLen);
            hexstr_to_byte(buff, buffLen, id);
            size_t idLen = buffLen / 2;
            SM9_SIGN_KEY sign_key = {0};
            // 生成签名密钥
            if (sm9_sign_master_key_extract_key(&sign_master, (char *)id, idLen, &sign_key) != 1) {
                LOG_PRINT("GenSm9KeyPair sm9_sign_master_key_extract_key error");
                break;
            }
            uint8_t buf[SM9_SIGN_KEY_SIZE] = {0};
            size_t bufLen = 0;
            uint8_t *p = buf;
            // 将生成的签名密钥数据序列化导出
            if (sm9_sign_key_to_der(&sign_key, &p, &bufLen) != 1) {
                LOG_PRINT("GenSm9KeyPair sm9_sign_key_to_der error");
                break;
            }
            LOG_PRINT_HEX("sign_key key:", buf, bufLen);
            // 得到密钥数据，示例 30 81 C9 03420004 私钥(64字节) 03818200 04 主密钥公钥(x+y=128字节)
            // 注：ds字段实际存储的是‌用户私钥的椭圆曲线点‌，而非公钥数据。还原时也进行归一化操作。
            // 拷贝密钥数据，返回结果
            byte_to_hexstr(buf, bufLen, buff);
            buffLen = bufLen * 2;
            break;
        }

        if (type == 0x11) { // 加解密主密钥
            SM9_ENC_MASTER_KEY enc_master = {0};
            // 生成加解密主密钥
            if (sm9_enc_master_key_generate(&enc_master) != 1) {
                LOG_PRINT("GenSm9KeyPair sm9_enc_master_key_generate error");
                break;
            }
            uint8_t buf[SM9_ENC_MASTER_KEY_MAX_SIZE] = {0};
            size_t bufLen = 0;
            uint8_t *p = buf;
            // 将生成的加解密主密钥数据序列化导出
            if (sm9_enc_master_key_to_der(&enc_master, &p, &bufLen) != 1) {
                LOG_PRINT("GenSm9KeyPair sm9_enc_master_key_to_der error");
                break;
            }
            LOG_PRINT_HEX("enc_master key:", buf, bufLen);
            // 得到密钥数据，示例 30 66 0220 私钥(32字节) 03420004 公钥(64字节)
            // 拷贝密钥数据，返回结果
            byte_to_hexstr(buf, bufLen, buff);
            buffLen = bufLen * 2;
            break;
        }

        if (type == 0x12) { // 加解密主密钥
            // 获取加解密主密钥数据
            uint8_t key[2048] = {0};
            napi_get_value_string_utf8(env, argv[1], buff, sizeof(key), &buffLen);
            hexstr_to_byte(buff, buffLen, key);
            size_t keyLen = buffLen / 2;
            LOG_PRINT_HEX("GenSm9KeyPair key:", key, keyLen)
            const uint8_t *cp = key;
            SM9_ENC_MASTER_KEY enc_master = {0};
            // 生成加解密主密钥
            if (sm9_enc_master_key_from_der(&enc_master, &cp, &keyLen) != 1) {
                LOG_PRINT("GenSm9KeyPair sm9_enc_master_key_from_der error");
                break;
            }
            // 获取ID数据
            unsigned char id[2048] = {0};
            napi_get_value_string_utf8(env, argv[2], buff, sizeof(id), &buffLen);
            hexstr_to_byte(buff, buffLen, id);
            size_t idLen = buffLen / 2;
            SM9_ENC_KEY enc_key = {0};
            // 生成加解密密钥
            if (sm9_enc_master_key_extract_key(&enc_master, (char *)id, idLen, &enc_key) != 1) {
                LOG_PRINT("GenSm9KeyPair sm9_enc_master_key_extract_key error");
                break;
            }
            uint8_t buf[SM9_ENC_KEY_SIZE] = {0};
            size_t bufLen = 0;
            uint8_t *p = buf;
            // 将生成的签名密钥数据序列化导出
            if (sm9_enc_key_to_der(&enc_key, &p, &bufLen) != 1) {
                LOG_PRINT("GenSm9KeyPair sm9_enc_key_to_der error");
                break;
            }
            LOG_PRINT_HEX("enc_key key:", buf, bufLen);
            // 得到密钥数据，示例 30 81 C9 0381820004 私钥(128字节) 03420004 公钥(64字节)
            // 拷贝密钥数据，返回结果
            byte_to_hexstr(buf, bufLen, buff);
            buffLen = bufLen * 2;
            break;
        }

    } while (0);

    napi_value result = nullptr;
    napi_create_string_utf8(env, buff, buffLen, &result);
    return result;
}

static napi_value NAPI_Global_Sm9Sign(napi_env env, napi_callback_info info) {
    memset(argv, 0, sizeof(argv));
    argc = 2;
    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);

    char buff[4096] = {0};
    size_t buffLen = 0;

    do {
        // 签名密钥
        uint8_t key[2048] = {0};
        napi_get_value_string_utf8(env, argv[0], buff, sizeof(key), &buffLen);
        hexstr_to_byte(buff, buffLen, key);
        size_t keyLen = buffLen / 2;
        LOG_PRINT_HEX("Sm9Sign key:", key, keyLen)
        // 签名原文
        uint8_t plain[2048] = {0};
        napi_get_value_string_utf8(env, argv[1], buff, sizeof(plain), &buffLen);
        hexstr_to_byte(buff, buffLen, plain);
        size_t plainLen = buffLen / 2;

        const uint8_t *cp = key;
        SM9_SIGN_KEY sign_key = {0};
        // 生成签名密钥
        if (sm9_sign_key_from_der(&sign_key, &cp, &keyLen) != 1) {
            LOG_PRINT("Sm9Sign sm9_sign_key_from_der error");
            break;
        }
        uint8_t sig[2048] = {0};
        size_t siglen = 0;
        SM9_SIGN_CTX sign_ctx;
        sm9_sign_init(&sign_ctx);
        sm9_sign_update(&sign_ctx, plain, plainLen);
        sm9_sign_finish(&sign_ctx, &sign_key, sig, &siglen);
        LOG_PRINT_HEX("Sm9Sign sig:", sig, siglen);
        // 返回签名结果 h+s数据
        byte_to_hexstr(sig, siglen, buff);
        buffLen = siglen * 2;

    } while (0);

    napi_value result = nullptr;
    napi_create_string_utf8(env, buff, buffLen, &result);
    return result;
}

static napi_value NAPI_Global_Sm9Verify(napi_env env, napi_callback_info info) {
    memset(argv, 0, sizeof(argv));
    argc = 4;
    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);

    char buff[4096] = {0};
    size_t buffLen = 0;
    bool rb = false;

    do {
        // 签名密钥
        uint8_t key[2048] = {0};
        napi_get_value_string_utf8(env, argv[0], buff, sizeof(key), &buffLen);
        hexstr_to_byte(buff, buffLen, key);
        size_t keyLen = buffLen / 2;
        LOG_PRINT_HEX("Sm9Verify key:", key, keyLen)
        // 签名原文
        uint8_t plain[2048] = {0};
        napi_get_value_string_utf8(env, argv[1], buff, sizeof(plain), &buffLen);
        hexstr_to_byte(buff, buffLen, plain);
        size_t plainLen = buffLen / 2;
        // 签名结果
        uint8_t sig[2048] = {0};
        napi_get_value_string_utf8(env, argv[2], buff, sizeof(sig), &buffLen);
        hexstr_to_byte(buff, buffLen, sig);
        size_t sigLen = buffLen / 2;
        // 用户id
        uint8_t id[2048] = {0};
        napi_get_value_string_utf8(env, argv[3], buff, sizeof(id), &buffLen);
        hexstr_to_byte(buff, buffLen, id);
        size_t idLen = buffLen / 2;

        const uint8_t *cp = key;
        SM9_SIGN_MASTER_KEY sign_master = {0};
//        SM9_SIGN_KEY sign_key = {0};
//        // 生成签名密钥
//        if (sm9_sign_key_from_der(&sign_key, &cp, &keyLen) != 1) {
//            LOG_PRINT("Sm9Sign sm9_sign_key_from_der error");
//            break;
//        }
//        // 拷贝签名主密钥公钥信息
//        sm9_twist_point_copy(&sign_master.Ppubs, &sign_key.Ppubs);
        if (sm9_sign_master_public_key_from_der(&sign_master, &cp, &keyLen) != 1) {
            LOG_PRINT("Sm9Sign sm9_sign_master_public_key_from_der error");
            break;
        }

        SM9_SIGN_CTX sign_ctx;
        sm9_sign_init(&sign_ctx);
        sm9_sign_update(&sign_ctx, plain, plainLen);
        int ret = sm9_verify_finish(&sign_ctx, sig, sigLen, &sign_master, (char *)id, idLen);
        LOG_PRINT("sm9_verify_finish ret:%{public}d", ret);
        rb = ret == 1;
    } while (0);

    napi_value result = nullptr;
    napi_get_boolean(env, rb, &result);
    return result;
}

static napi_value NAPI_Global_Sm9Cipher(napi_env env, napi_callback_info info) {
    memset(argv, 0, sizeof(argv));
    argc = 4;
    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);

    char buff[4096] = {0};
    size_t buffLen = 0;

    do {
        int type = 0; // 0x01 加密，0x02 解密
        napi_get_value_int32(env, argv[0], &type);
        if (type != 0x01 && type != 0x02) {
            LOG_PRINT("Sm9Cipher type:%{public}d error", type);
            break;
        }

        // 加解密密钥
        uint8_t key[2048] = {0};
        napi_get_value_string_utf8(env, argv[1], buff, sizeof(key), &buffLen);
        hexstr_to_byte(buff, buffLen, key);
        size_t keyLen = buffLen / 2;
        LOG_PRINT_HEX("Sm9Cipher key:", key, keyLen)
        // 用户id
        uint8_t id[2048] = {0};
        napi_get_value_string_utf8(env, argv[2], buff, sizeof(id), &buffLen);
        hexstr_to_byte(buff, buffLen, id);
        size_t idLen = buffLen / 2;
        // 输入数据
        uint8_t input[2048] = {0};
        napi_get_value_string_utf8(env, argv[3], buff, sizeof(input), &buffLen);
        hexstr_to_byte(buff, buffLen, input);
        size_t inputLen = buffLen / 2;

        const uint8_t *cp = key;
        SM9_ENC_MASTER_KEY enc_master = {0};
        SM9_ENC_KEY enc_key = {0};

        uint8_t output[2048] = {0};
        size_t outputlen = 0;
        if (type == 0x01) { // 加密
            if (sm9_enc_master_public_key_from_der(&enc_master, &cp, &keyLen) != 1) {
                LOG_PRINT("Sm9Sign sm9_sign_master_public_key_from_der error");
                break;
            }
            // 加密
            sm9_encrypt(&enc_master, (char *)id, idLen, input, inputLen, output, &outputlen);
        }
        if (type == 0x02) { // 解密
            // 生成加解密密钥
            if (sm9_enc_key_from_der(&enc_key, &cp, &keyLen) != 1) {
                LOG_PRINT("Sm9Cipher sm9_enc_key_from_der error");
                break;
            }
            // 解密
            sm9_decrypt(&enc_key, (char *)id, idLen, input, inputLen, output, &outputlen);
        }
        LOG_PRINT_HEX("Sm9Cipher output:", output, outputlen);
        // 返回结果
        byte_to_hexstr(output, outputlen, buff);
        buffLen = outputlen * 2;

    } while (0);

    napi_value result = nullptr;
    napi_create_string_utf8(env, buff, buffLen, &result);
    return result;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("GetRandomBuffer", NAPI_Global_GetRandomBuffer),
        DECLARE_NAPI_FUNCTION("GetRandomBufferSync", NAPI_Global_GetRandomBufferSync),
        DECLARE_NAPI_FUNCTION("ZucCrypto", NAPI_Global_ZucCrypto),
        DECLARE_NAPI_FUNCTION("GenSm9KeyPair", NAPI_Global_GenSm9KeyPair),
        DECLARE_NAPI_FUNCTION("Sm9Sign", NAPI_Global_Sm9Sign),
        DECLARE_NAPI_FUNCTION("Sm9Verify", NAPI_Global_Sm9Verify),
        DECLARE_NAPI_FUNCTION("Sm9Cipher", NAPI_Global_Sm9Cipher),
    };
//    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]),
    //    desc);
    const char className[] = "CryptoNativeAPI";
    napi_value classObject = nullptr;
    napi_define_class(env, className, sizeof(className), Constructor, nullptr, sizeof(desc) / sizeof(desc[0]), desc,
                      &classObject);
    napi_set_named_property(env, exports, className, classObject);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "letter_crypto",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterLetter_cryptoModule(void) { napi_module_register(&demoModule); }
